/*****************************************************************************/

function  DoNCategoryFit (weightMatrix)
{
	weightClasses	 = Rows(weightMatrix);
	breakPointMatrix = {3,weightClasses};
	
	runningSum 		= 0;
	targetSum  		= weightMatrix[0];
	currentIndex 	= 0;
	currentSlider	= 0;
	runningSize		= 1;
	valueSum		= 0;
	currentSpan		= SELECTED_CHART_DATA[1][currentIndex];
	
	measCount 		= Columns (SELECTED_CHART_DATA);
	
	
	while (currentIndex < measCount - 1)
	{
		runningSum = runningSum + SELECTED_CHART_DATA[1][currentIndex]/dataPoints;
		
		if ((runningSum >= targetSum)||(measCount-currentIndex <= weightClasses - currentSlider))
		{
			breakPointMatrix[0][currentSlider] = currentIndex;
			breakPointMatrix[1][currentSlider] = runningSize;
				
			breakPointMatrix[2][currentSlider] = (SELECTED_CHART_DATA[0][currentIndex]*SELECTED_CHART_DATA[1][currentIndex]+valueSum)/(currentSpan+SELECTED_CHART_DATA[1][currentIndex]);	
			runningSize   = 1;	
			valueSum	  = 0;
			currentSlider = currentSlider + 1;
			targetSum 	  = targetSum + weightMatrix[currentSlider];
			currentSpan	  = 0;
		}	
		else
		{
			valueSum	= valueSum	  + SELECTED_CHART_DATA[0][currentIndex]*SELECTED_CHART_DATA[1][currentIndex];
			runningSize = runningSize + 1;
			currentSpan = currentSpan + SELECTED_CHART_DATA[1][currentIndex];
		}
		currentIndex = currentIndex + 1;
	}

	currentSpan	  = currentSpan + SELECTED_CHART_DATA[1][currentIndex];
	valueSum = valueSum	+ SELECTED_CHART_DATA[0][currentIndex]*SELECTED_CHART_DATA[1][currentIndex];
	breakPointMatrix[0][currentSlider] = currentIndex;
	breakPointMatrix[1][currentSlider] = runningSize;	
	breakPointMatrix[2][currentSlider] = valueSum/currentSpan;	
	
		
	currentIndex 	= 0;
	currentSlider	= 0;
	runningOffset	= 0;
	
	/* now apply the normal smear */

	logLikelihood	= 0;
	valueSum		= Rows(weightMatrix);
	
	while (currentSlider < valueSum)
	{
		classSize 	= breakPointMatrix[1][currentSlider];
		classWeight = weightMatrix[currentSlider];
			
		if (classWeight > 0.0)
		{
			if (classSize == 1)
			/* a single class representative - no smearing needed */
			{
				logLikelihood = logLikelihood + SELECTED_CHART_DATA[1][runningOffset] * Log (weightMatrix[currentSlider]);
			}
			else
			{
				classMean 		= breakPointMatrix[2][currentSlider];
				classNorm 		= 0;
				currentIndex 	= runningOffset+classSize-1;
					
				REWEIGHTED_MATRIX = {measCount,1};
				for (reslider = runningOffset; reslider < currentIndex; reslider = reslider+1)
				{
					targetSum = SELECTED_CHART_DATA[0][reslider];
					targetSum = Exp(-(targetSum-classMean)^2/(2*varMult*Abs(classMean)));		
					if (targetSum==0)
					{
						targetSum = 1e-50;
					}			
					REWEIGHTED_MATRIX[reslider] = targetSum;
					classNorm = classNorm + targetSum;
				}
				
				targetSum = SELECTED_CHART_DATA[0][reslider];
				targetSum = Exp(-(targetSum-classMean)^2/(2*varMult*Abs(classMean)));		
				if (targetSum==0)
				{
					targetSum = 1e-50;
				}			

				REWEIGHTED_MATRIX[reslider] = targetSum;
				classNorm = classNorm + targetSum;
				
				REWEIGHTED_MATRIX = Log (REWEIGHTED_MATRIX * (classWeight/classNorm));								

				currentIndex = currentIndex + 1;
				for (reslider = runningOffset; reslider < currentIndex; reslider = reslider+1)
				{
					logLikelihood = logLikelihood + REWEIGHTED_MATRIX[reslider]*SELECTED_CHART_DATA[1][reslider];
				}
				
			}
		}
		else
		{
			if (classSize>0)
			{
				return -(1e100);
			}
		}
		runningOffset = runningOffset + classSize;
		currentSlider = currentSlider + 1;
	}
	
	return logLikelihood;
}

/*****************************************************************************/

if (NON_EMPTY_SELECTION)
{
	UPDATE_CELL_DATE = 0;
	
	data_rows  	 = Columns 	(SELECTED_CHART_ROWS);

	temp = SELECTED_CHART_COLS[0];

	for (count = 1; count<data_rows; count = count+1)
	{
		temp2 = SELECTED_CHART_COLS[count];
		if (temp!=temp2)
		{
			break;
		}
		if (SELECTED_CHART_DATA[count]<SELECTED_CHART_DATA[count-1])
		{
			break;
		}
	}

	if (count == data_rows)
	{
		/* compress the data vector to account for repeated observations */
		
		dataPoints = Columns(SELECTED_CHART_DATA);
		temp_data_vector = {2, dataPoints};
		currentIndex = 0;
		
		temp_data_vector[0][0] = SELECTED_CHART_DATA[0];
		temp_data_vector[1][0] = 1;
		for (nextIndex = 1; nextIndex < dataPoints; nextIndex = nextIndex + 1)
		{
			if (SELECTED_CHART_DATA[nextIndex]!=SELECTED_CHART_DATA[nextIndex-1])
			{
				currentIndex = currentIndex+1;
				temp_data_vector[0][currentIndex] = SELECTED_CHART_DATA[nextIndex];
			}
			temp_data_vector[1][currentIndex] = temp_data_vector[1][currentIndex] + 1;
		}
		
		SELECTED_CHART_DATA = {2, currentIndex+1};
		
		upperBound = 0;
		
		for (nextIndex = 0; nextIndex <= currentIndex; nextIndex = nextIndex + 1)
		{
			SELECTED_CHART_DATA[0][nextIndex] = temp_data_vector[0][nextIndex];
			SELECTED_CHART_DATA[1][nextIndex] = temp_data_vector[1][nextIndex];
			upperBound = upperBound + temp_data_vector[1][nextIndex] * Log(temp_data_vector[1][nextIndex]/dataPoints);
		}
		
		
		temp_data_vector = 0;
		normal_sigma 	 = .25;
		lastMax 		 = -100000000000;

		for (resp = 2; resp <= Columns(SELECTED_CHART_DATA); resp = resp+1)
		{
			freqStrMx    = {resp,1};
			freqStrMx[0] = "PS_1";

			for (mi=1; mi<resp-1; mi=mi+1)
			{
				freqStrMx[mi] = "";
				for (mi2=1;mi2<=mi;mi2=mi2+1)
				{
					freqStrMx[mi] = freqStrMx[mi]+"(1-PS_"+mi2+")";		
				}
				freqStrMx[mi] = freqStrMx[mi]+"PS_"+(mi+1);	
			}	

			freqStrMx[mi] = "";
			for (mi2=1;mi2<mi;mi2=mi2+1)
			{
				freqStrMx[mi] = freqStrMx[mi]+"(1-PS_"+mi2+")";		
			}
			freqStrMx[mi] = freqStrMx[mi]+"(1-PS_"+mi+")";	
			mx = {resp,1};
			
			execString = "";
			for (resp2 = 0; resp2 < resp; resp2 = resp2 + 1)
			{
				if (resp2)
				{
					execString = execString + "PS_"+resp2+":>0;PS_"+resp2+":<1;PS_"+resp2+"=1/"+(resp-resp2+1)+";";
				}
				execString = execString + "mx[" + resp2 + "]:=" + freqStrMx[resp2] + ";";
			}
			ExecuteCommands (execString);
			
			varMult = 1+Log(resp-1);
			
			/*mx = {resp,1};
			nStr = "";
			
			for (mi=1; mi<=resp; mi=mi+1)
			{
				ExecuteCommands ("APS_"+mi+"="+1/resp+";APS_"+mi+":>0;APS_"+mi+":<1;mx["+(mi-1)+"]:=APS_"+mi+"/weightFactor;");
				nStr = nStr + "+APS_"+mi;
			}*/
			
			ExecuteCommands ("weightFactor:="+nStr[1][Abs(nStr)-1]+";");
			Optimize (bestFit, DoNCategoryFit(mx));
			
			fprintf (stdout, Format (resp,3,0), " rate classes: Log(L) = ", bestFit[1][0], " (upper bound = ", upperBound, ")\n");
			if (bestFit[1][0] - lastMax < 2)
			{
				break;
			}
			saveBPM = breakPointMatrix;
			saveMx	= mx;
			lastMax = bestFit[1][0];
		}
		
		resp = resp-1;
		
		fprintf (stdout, "\n\nFit ", resp, " categories to the data\n\n");
		
		for (resp2 = 0; resp2 < resp; resp2 = resp2+1)
		{
			fprintf (stdout, "Class ", Format (resp2+1,3,0), " : ",  Format (saveBPM[2][resp2], 8, 5), " weight = ", Format (saveMx[resp2], 8, 5), "\n");
		}
	}
	else
	{
		fprintf (stdout, "Please select a single sorted column to fit a profile to.\n");
	}
}
else
{
	NON_EMPTY_SELECTION = -1;
}
